#include "Controller.h"
#include "../logger/Exception.h"

#include <iostream>

namespace Network {

Controller::Controller(int port) : port_(port) {
	next_id_ = 1;
	mainThread = QThread::currentThread();
}

void Controller::run() {
	bool timed_out = false;

	// tworzymy serwer
	tcpServer = new QTcpServer;

	// rozpoczynamy nasłuchiwanie na wszystkich interfejsach
	// na podanym numerze portu
	if (!tcpServer->listen(QHostAddress::Any, port_)) {
		throw Logger::Exception(tcpServer->serverError(), tcpServer->errorString());
	}

	emit newMsg("Watek kontrolera sieci rozpoczety, serwer nasluchuje", Logger::mtInfo);

	std::cout << "Serwer dziala na porcie " << tcpServer->serverPort() << "\n";

	active_ = true;

	while (active_) {

		/*
		// sprawdzamy, czy jest jakieś nowe połączenie oczekujące
		if (tcpServer->hasPendingConnections())
			onNewConnection();
		*/

		// blokujemy się w oczekiwaniu na nowe połączenie przychodzące na pół sekundy
		tcpServer->waitForNewConnection(500, &timed_out);
		if (!timed_out)
			onNewConnection();

		// zasypiamy na chwilę, żeby dać czas innym wątkom
		msleep(10);
	}

	onFinished();
}

void Controller::stop() {
	active_ = false;
}

void Controller::sendToAll(Packet * packet) {
	foreach (RemoteClient * client, clients_)
		client->send(packet);
}

RemoteClient * Controller::getOutgoingByIp(int ip) {
	foreach (RemoteClient * client, outgoing_) {
		if (client->getHostAddress() == QHostAddress(ip))
			return client;
	}

	return NULL;
}

RemoteClient * Controller::getIncomingById(int id) {
	foreach (RemoteClient * client, clients_) {
		if (client->id() == id)
			return client;
	}

	return NULL;
}

RemoteClient * Controller::getOutgoingById(int id) {
	foreach (RemoteClient * client, outgoing_) {
		if (client->id() == id)
			return client;
	}

	return NULL;
}

RemoteClient * Controller::connectToHost(QString host_name, int port, int id) {
	QTcpSocket * socket = new QTcpSocket(this);
	socket->connectToHost(host_name, port);

	// czekamy 10 sekund na połączenie, potem się wykrzaczamy
	if (!socket->waitForConnected(10*1000)) {
		emit newMsg(socket->errorString(), Logger::mtError);
		//delete socket;
		return NULL;
	}

	socket->setParent(0);
	socket->moveToThread(mainThread);

	RemoteClient * client = new RemoteClient(socket, next_id_);

	if (id >=0)
		client->id(id);
	else
		client->id(-1);

	client->moveToThread(mainThread);

	outgoing_.insert(next_id_, client);
	++next_id_;

	// łapiemy zdarzenie związane z odebraniem pakietu przez klienta
	connect(client, SIGNAL(newPacket(RemoteClient *)), this, SLOT(onNewPacket(RemoteClient *)));

	// klient umarł, trzeba sobie jakoś poradzić z jego zwłokami
	connect(client, SIGNAL(clientDied(RemoteClient*)), this, SLOT(onClientDied(RemoteClient*)));

	client->start();

	return client;
}

RemoteClient * Controller::connectToHostByIp(QHostAddress addr, int port, int id) {
	QTcpSocket * socket = new QTcpSocket(this);
	socket->connectToHost(addr, port);

	std::cout << "Łączę się z " << addr.toString().toStdString() << "...";

	// czekamy 10 sekund na połączenie, potem się wykrzaczamy
	if (!socket->waitForConnected(10*1000)) {
		emit newMsg(socket->errorString(), Logger::mtError);
		return NULL;
	}

	socket->setParent(0);
	socket->moveToThread(mainThread);

	RemoteClient * client = new RemoteClient(socket, next_id_);

	if (id >=0)
		client->id(id);
	else
		client->id(-1);

	client->moveToThread(mainThread);

	outgoing_.insert(next_id_, client);
	++next_id_;

	// łapiemy zdarzenie związane z odebraniem pakietu przez klienta
	connect(client, SIGNAL(newPacket(RemoteClient *)), this, SLOT(onNewPacket(RemoteClient *)));

	// klient umarł, trzeba sobie jakoś poradzić z jego zwłokami
	connect(client, SIGNAL(clientDied(RemoteClient*)), this, SLOT(onClientDied(RemoteClient*)));

	client->start();

	return client;
}


void Controller::onClientDied(RemoteClient * client) {
	emit newMsg(client->getAddr() + ": " + client->socket()->errorString(), Logger::mtError);
	client->stop();
	client->wait(1000);

	// wyszukujemy klienta i usuwamy z obu list

	foreach(RemoteClient * dead, clients_)
		if (dead == client) {
			std::cout << "Klienta " << client->getAddr().toStdString() << " usunięto z listy\n";
			clients_.remove(clients_.key(dead));
		}

	foreach(RemoteClient * dead, outgoing_)
		if (dead == client) {
			std::cout << "Klienta " << client->getAddr().toStdString() << " usunięto z listy out\n";
			outgoing_.remove(outgoing_.key(dead));
		}

	foreach(RemoteClient * dead, outgoing_)
		if (dead == client) {
			std::cout << "Ale on ciągle tam jest!:/ \n";
		}

	emit clientDied(client);
}

void Controller::removeClient(RemoteClient * client) {
	//emit newMsg(client->getAddr() + ": " + client->socket()->errorString(), Logger::mtError);
	client->stop();
	client->wait(1000);
	clients_.remove(client->id());
}

void Controller::onNewConnection() {
	// tworzymy nowego klienta, jego połączenie jest zrealizowane na gnieździe zwróconym przez serwer
	QTcpSocket * socket = tcpServer->nextPendingConnection();

	socket->setParent(0);
	socket->moveToThread(mainThread);

	RemoteClient * client = new RemoteClient(socket, next_id_);

	client->moveToThread(mainThread);

	// dodajemy klienta do listy

	clients_.insert(next_id_, client);
	++next_id_;

	// łapiemy zdarzenie związane z odebraniem pakietu przez klienta
	connect(client, SIGNAL(newPacket(RemoteClient *)), this, SLOT(onNewPacket(RemoteClient *)));

	// klient umarł, trzeba sobie jakoś poradzić z jego zwłokami
	connect(client, SIGNAL(clientDied(RemoteClient*)), this, SLOT(onClientDied(RemoteClient*)));

	// zgłaszamy fakt podłączenia nowego klienta
	emit newClient(client);

	// uruchamiamy wątek tego klienta
	client->start();
}

void Controller::onNewPacket(RemoteClient * client) {
	// przesyłamy dalej informację o nowym pakiecie
	emit newPacket(client);
}

void Controller::onFinished() {
	RemoteClient * client;

	foreach(client, clients_) {
		// kończymy wątek klienta, dajemy mu sekundę na zamknięcie się
		client->stop();
		client->wait(1000);
		delete client;
	}

	foreach(client, outgoing_) {
		// kończymy wątek klienta, dajemy mu sekundę na zamknięcie się
		client->stop();
		client->wait(1000);
		delete client;
	}

	emit newMsg("Watek kontrolera sieci zakonczony pomyslnie", Logger::mtInfo);
}

} // namespace Network
